///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Contains a simple CLContainer class.
 *	\file		IceContainer.cpp
 *	\author		Pierre Terdiman
 *	\date		February, 5, 2000
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Contains a list of 32-bits values.
 *	Use this class when you need to store an unknown number of values. The list is automatically
 *	resized and can contains 32-bits entities (dwords or floats)
 *
 *	\class		Container
 *	\author		Pierre Terdiman
 *	\version	1.0
 *	\date		08.15.98
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Precompiled Header

// Static members
#include "stdafx.h"

#include "LaborMathInclude.h"
#include "LaborContainer.h"


NAMESPACE_LABOR3D_BEGIN
#ifdef CONTAINER_STATS
unsigned int CLContainer::mNbContainers = 0;
unsigned int CLContainer::mUsedRam = 0;
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Constructor. No entries allocated there.
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CLContainer::CLContainer() : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2.0f)
{
#ifdef CONTAINER_STATS
	mNbContainers++;
	mUsedRam+=sizeof(CLContainer);
#endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Constructor. Also allocates a given number of entries.
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CLContainer::CLContainer(unsigned int size, float growth_factor) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(growth_factor)
{
#ifdef CONTAINER_STATS
	mNbContainers++;
	mUsedRam+=sizeof(CLContainer);
#endif
	setSize(size);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Copy constructor.
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CLContainer::CLContainer(const CLContainer& object) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2.0f)
{
#ifdef CONTAINER_STATS
	mNbContainers++;
	mUsedRam+=sizeof(CLContainer);
#endif
	*this = object;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Destructor.	Frees everything and leaves.
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CLContainer::~CLContainer()
{
	empty();
#ifdef CONTAINER_STATS
	mNbContainers--;
	mUsedRam-=getUsedRam();
#endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Clears the container. All stored values are deleted, and it frees used ram.
 *	\see		Reset()
 *	\return		Self-Reference
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CLContainer& CLContainer::empty()
{
#ifdef CONTAINER_STATS
	mUsedRam-=mMaxNbEntries*sizeof(unsigned int);
#endif
	SafeDeleteArray(mEntries);
	mCurNbEntries = mMaxNbEntries = 0;
	return *this;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Resizes the CLContainer.
 *	\param		needed	[in] assume the container can be added at least "needed" values
 *	\return		true if success.
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool CLContainer::resize(unsigned int needed)
{
#ifdef CONTAINER_STATS
	// Subtract previous amount of bytes
	mUsedRam-=mMaxNbEntries*sizeof(unsigned int);
#endif

	// Get more entries
	mMaxNbEntries = mMaxNbEntries ? unsigned int(float(mMaxNbEntries)*mGrowthFactor) : 2;	// Default nb Entries = 2
	if(mMaxNbEntries<mCurNbEntries + needed)	mMaxNbEntries = mCurNbEntries + needed;

	// Get some bytes for new entries
	unsigned int*	NewEntries = new unsigned int[mMaxNbEntries];
	CHECKALLOC(NewEntries);

#ifdef CONTAINER_STATS
	// Add current amount of bytes
	mUsedRam+=mMaxNbEntries*sizeof(unsigned int);
#endif

	// Copy old data if needed
	if(mCurNbEntries)	CopyMemory(NewEntries, mEntries, mCurNbEntries*sizeof(unsigned int));

	// Delete old data
	SafeDeleteArray(mEntries);

	// Assign new pointer
	mEntries = NewEntries;

	return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Sets the initial size of the container. If it already contains something, it's discarded.
 *	\param		nb		[in] Number of entries
 *	\return		true if success
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool CLContainer::setSize(unsigned int nb)
{
	// Make sure it's empty
	empty();

	// Checkings
	if(!nb)	return false;

	// Initialize for nb entries
	mMaxNbEntries = nb;

	// Get some bytes for new entries
	mEntries = new unsigned int[mMaxNbEntries];
	CHECKALLOC(mEntries);

#ifdef CONTAINER_STATS
	// Add current amount of bytes
	mUsedRam+=mMaxNbEntries*sizeof(unsigned int);
#endif
	return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Refits the container and get rid of unused bytes.
 *	\return		true if success
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool CLContainer::refit()
{
#ifdef CONTAINER_STATS
	// Subtract previous amount of bytes
	mUsedRam-=mMaxNbEntries*sizeof(unsigned int);
#endif

	// Get just enough entries
	mMaxNbEntries = mCurNbEntries;
	if(!mMaxNbEntries)	return false;

	// Get just enough bytes
	unsigned int*	NewEntries = new unsigned int[mMaxNbEntries];
	CHECKALLOC(NewEntries);

#ifdef CONTAINER_STATS
	// Add current amount of bytes
	mUsedRam+=mMaxNbEntries*sizeof(unsigned int);
#endif

	// Copy old data
	CopyMemory(NewEntries, mEntries, mCurNbEntries*sizeof(unsigned int));

	// Delete old data
	SafeDeleteArray(mEntries);

	// Assign new pointer
	mEntries = NewEntries;

	return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Checks whether the container already contains a given value.
 *	\param		entry			[in] the value to look for in the container
 *	\param		location		[out] a possible pointer to store the entry location
 *	\see		Add(unsigned int entry)
 *	\see		Add(float entry)
 *	\see		Empty()
 *	\return		true if the value has been found in the container, else false.
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool CLContainer::contains(unsigned int entry, unsigned int* location) const
{
	// Look for the entry
	for(unsigned int i=0;i<mCurNbEntries;i++)
	{
		if(mEntries[i]==entry)
		{
			if(location)	*location = i;
			return true;
		}
	}
	return false;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Deletes an entry. If the container contains such an entry, it's removed.
 *	\param		entry		[in] the value to delete.
 *	\return		true if the value has been found in the container, else false.
 *	\warning	This method is arbitrary slow (O(n)) and should be used carefully. Insertion order is not preserved.
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool CLContainer::deleteEntry(unsigned int entry)
{
	// Look for the entry
	for(unsigned int i=0;i<mCurNbEntries;i++)
	{
		if(mEntries[i]==entry)
		{
			// Entry has been found at index i. The strategy is to copy the last current entry at index i, and decrement the current number of entries.
			DeleteIndex(i);
			return true;
		}
	}
	return false;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Deletes an entry, preserving the insertion order. If the container contains such an entry, it's removed.
 *	\param		entry		[in] the value to delete.
 *	\return		true if the value has been found in the container, else false.
 *	\warning	This method is arbitrary slow (O(n)) and should be used carefully.
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool CLContainer::deleteKeepingOrder(unsigned int entry)
{
	// Look for the entry
	for(unsigned int i=0;i<mCurNbEntries;i++)
	{
		if(mEntries[i]==entry)
		{
			// Entry has been found at index i.
			// Shift entries to preserve order. You really should use a linked list instead.
			mCurNbEntries--;
			for(unsigned int j=i;j<mCurNbEntries;j++)
			{
				mEntries[j] = mEntries[j+1];
			}
			return true;
		}
	}
	return false;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Gets the next entry, starting from input one.
 *	\param		entry		[in/out] On input, the entry to look for. On output, the next entry
 *	\param		find_mode	[in] wrap/clamp
 *	\return		Self-Reference
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CLContainer& CLContainer::findNext(unsigned int& entry, ELFindMode find_mode)
{
	unsigned int Location;
	if(contains(entry, &Location))
	{
		Location++;
		if(Location==mCurNbEntries)	Location = find_mode==FIND_WRAP ? 0 : mCurNbEntries-1;
		entry = mEntries[Location];
	}
	return *this;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Gets the previous entry, starting from input one.
 *	\param		entry		[in/out] On input, the entry to look for. On output, the previous entry
 *	\param		find_mode	[in] wrap/clamp
 *	\return		Self-Reference
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CLContainer& CLContainer::findPrev(unsigned int& entry, ELFindMode find_mode)
{
	unsigned int Location;
	if(contains(entry, &Location))
	{
		Location--;
		if(Location==0xffffffff)	Location = find_mode==FIND_WRAP ? mCurNbEntries-1 : 0;
		entry = mEntries[Location];
	}
	return *this;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 *	Gets the ram used by the container.
 *	\return		the ram used in bytes.
 */
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsigned int CLContainer::getUsedRam() const
{
	return sizeof(CLContainer) + mMaxNbEntries * sizeof(unsigned int);
}

/*void CLContainer::operator=(const CLContainer& object)
{
	SetSize(object.GetNbEntries());
	CopyMemory(mEntries, object.GetEntries(), mMaxNbEntries*sizeof(unsigned int));
	mCurNbEntries = mMaxNbEntries;
}*/

NAMESPACE_LABOR3D_END